home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / AppsToGo / Kibitz / Chess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  58.2 KB  |  2,134 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        chess.c
  5. ** Written by:  Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved. */
  9.  
  10.  
  11.  
  12. /*****************************************************************************/
  13.  
  14.  
  15.  
  16. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  17. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  18. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  19.  
  20. #ifndef __ERRORS__
  21. #include <Errors.h>
  22. #endif
  23.  
  24. #ifndef __FONTS__
  25. #include <Fonts.h>
  26. #endif
  27.  
  28. #ifndef __TEXTEDITCONTROL__
  29. #include <TextEditControl.h>
  30. #endif
  31.  
  32. #ifndef __TOOLUTILS__
  33. #include <ToolUtils.h>
  34. #endif
  35.  
  36. #ifndef __STDIO__
  37. #include <StdIO.h>
  38. #endif
  39.  
  40. #ifndef __STRING__
  41. #include <String.h>
  42. #endif
  43.  
  44. #ifndef THINK_C
  45. #ifndef __STRINGS__
  46. #include <Strings.h>
  47. #endif
  48. #endif
  49.  
  50. #ifndef __UTILITIES__
  51. #include <Utilities.h>
  52. #endif
  53.  
  54.  
  55.  
  56. /*****************************************************************************/
  57.  
  58.  
  59.  
  60. extern Boolean    gComputerResigns;
  61.  
  62. short    gPieceLoc;
  63.  
  64. #define kLastNode         6
  65. #define kComputerResigns -9999
  66.  
  67. static GameListHndl        gGenMovesHndl;
  68. static unsigned long    idleTick;
  69. static MoveListHndl        gNodeHndl[kLastNode + 1];
  70.  
  71. static short    gNumPieces, gPosReps;
  72. static long    gTreeValue, gWhiteTotal, gBlackTotal;
  73. static long    gTreePieceValues[13] = {
  74.     -0x40000000L, -0x00090000L, -0x00050000L, -0x00030000L, -0x00030000L, -0x00010000L,
  75.      0x00000000L,
  76.      0x00010000L,  0x00030000L,  0x00030000L,  0x00050000L,  0x00090000L,  0x40000000L,
  77. };
  78.  
  79. static short    distance[10] = {0, 1, 1, 7, 7, 7, 1};
  80.     /* How far a piece can move.                        */
  81.     /* The double-pawn-push is handled as an exception. */
  82.     /* Castling is handled as an exception.                */
  83.  
  84. static short    direction[10][9] = {
  85.       0,   0,   0,   0,   0,   0,   0,   0,   0,
  86.      10,   9,  11,   0,   0,   0,   0,   0,   0,    /* Pawn moves.     */
  87.     -21, -19, -12,  -8,   8,  12,  19,  21,   0,    /* Knight moves. */
  88.     -11,  -9,   9,  11,   0,   0,   0,   0,   0,    /* Bishop moves. */
  89.     -10,  -1,   1,  10,   0,   0,   0,   0,   0,    /* Rook moves.     */
  90.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* Queen moves.     */
  91.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* King moves.     */
  92. };
  93.  
  94. static short    gPieceColor[13] = {WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
  95.                                    EMPTY,
  96.                                    BLACK, BLACK, BLACK, BLACK, BLACK, BLACK};
  97.  
  98. static short    gPieceKind[13]  = {KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN,
  99.                                    EMPTY,
  100.                                    PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING};
  101.  
  102.  
  103.  
  104. /*****************************************************************************/
  105.  
  106.  
  107.  
  108. TheDoc    newDocData
  109. #ifndef __PPCC__
  110.     /*
  111.         Work around compiler bug with aggregate initializers of
  112.         mixed alignment structures (68k structs containing PowerPC
  113.         structs).
  114.     */
  115.  
  116.  = {
  117.     kVersion,        /* File format version.                             */
  118.  
  119.     false,            /* Flag indicating print record is current.         */
  120.     {                /* Space for print record.                         */
  121.         0,
  122.         {0, 0, 0,{0, 0, 0, 0},},
  123.         {0, 0, 0, 0},
  124.         {0, 0, 0, 0, 0},
  125.         {0, 0, 0,{0, 0, 0, 0},},
  126.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  127.         {0, 0, 0, 0, 0, nil, nil, 0, 0, 0},
  128.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  129.     },
  130.  
  131.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  132.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  133.     OBNDS,  BR,    BN,    BB,    BQ,    BK,    BB,    BN,    BR,   OBNDS,
  134.     OBNDS,  BP,    BP,    BP,    BP,    BP,    BP,    BP,    BP,   OBNDS,
  135.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  136.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  137.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  138.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  139.     OBNDS,  WP,    WP,    WP,    WP,    WP,    WP,    WP,    WP,   OBNDS,
  140.     OBNDS,  WR,    WN,    WB,    WQ,    WK,    WB,    WN,    WR,   OBNDS,
  141.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  142.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  143.     {
  144.         WKPOS, 0,    /* White king position, king-moved count.         */
  145.         0,            /* White queen-rook-moved count.                 */
  146.         0,            /* White king-rook-moved count.                     */
  147.         BKPOS, 0,    /* Black king position, king-moved count.         */
  148.         0,            /* Black queen-rook-moved count.                 */
  149.         0            /* Black king-rook-moved count.                     */
  150.     },
  151.     0,                /* En-passant opportunity location.                 */
  152.     0,                /* En-passant opportunity pawn-to-take location. */
  153.     0,                /* Arranged en-passant opportunity loc.             */
  154.     0,                /* Arranged en-passant opp. pawn-to-take loc.     */
  155.     0,                /* Number of legal moves in move list.             */
  156.     0,                /* Index into record of game.                     */
  157.     0,                /* Number of moves in game.                         */
  158.     0,                /* My color (0 = white).                         */
  159.     0,                /* True if black started game.                     */
  160.     0,                /* True if in arrange-board mode.                 */
  161.     WP,                /* Piece hilited in arrange-board palette.         */
  162.     18000L,            /* 5 minute default time for white.                 */
  163.     18000L,            /* 5 minute default time for black.                 */
  164.     -1L,            /* Ticks remaining for white. (-1, no clock)     */
  165.     -1L,            /* Ticks remaining for black. (-1, no clock)     */
  166.     false,            /* Display board normal (not inverted.)             */
  167.     0,                /* Above info is saved to disk.                     */
  168.  
  169.     "\0",            /* Space for opponent zone.                         */
  170.     "\0",            /* Space for opponent machine.                     */
  171.     "\0",            /* Space for opponent kibitz full path.             */
  172.     "\0",            /* Space for opponent kibitz filename.             */
  173.     false,            /* Boolean for just board window.                 */
  174.     false,            /* Boolean for document is template.             */
  175.     false,            /* Document saved while computer moves white.     */
  176.     false,            /* Document saved while computer moves black.     */
  177.     0,                /* Above is new version info saved to disk.         */
  178.  
  179.     false,            /* Flag indicating existence of opponent.         */
  180.     0L,                /* ID assigned by me for this game.                 */
  181.     0L,                /* ID assigned by opponent for this game.         */
  182.     0,                /* Reason for sending the game.                     */
  183.     0,                /* State of the draw button.                      */
  184.     {
  185.         0L,            /* AEAddressDesc of opponent.                     */
  186.         nil
  187.     },
  188.     0,                /* Above is send game info.                         */
  189.  
  190.     0,                /* State for receiving AppleEvents.                 */
  191.     false,            /* Flag indicating if we originated game.         */
  192.     -1L,            /* Ticks remaining displayed for white.             */
  193.     -1L,            /* Ticks remaining displayed for black.             */
  194.     -1L,            /* Ticks for freeze clock for white.             */
  195.     -1L,            /* Ticks for freeze clock for black.             */
  196.     0L,                /* Reference tick for timer.                     */
  197.     0L,                /* Tick when computer moved last.                 */
  198.     0L,                /* Tick when received last info from opponent.     */
  199.     false,            /* Flag indicating computer moves white pieces.     */
  200.     false,            /* Flag indicating computer moves black pieces.     */
  201.     "\0",            /* Space for opponent name.                         */
  202.     "\0",            /* Space for opponent zone.                         */
  203.     "\0",            /* Space for opponent machine.                     */
  204.     0,                /* Time that last move/message received.         */
  205.     0,                /* Above info is for one machine only.             */
  206.  
  207.     false,            /* Flag indicating color change has been posted. */
  208.     0,                /* New color from config.                         */
  209.     false,            /* Flag indicating time change has been posted.     */
  210.     -1L,            /* New white time from config.                     */
  211.     -1L,            /* New black time from config.                     */
  212.     0,                /* Above info is config setting which will         */
  213.                     /* not be applied until a NULL event.             */
  214.  
  215.     nil,            /* Handle to legal move list.                     */
  216.     nil,            /* Handle to game record.                         */
  217.     nil,            /* Handle to incoming message.                     */
  218.     nil,            /* Handle to outgoing message.                     */
  219.     nil,            /* Handle to recorded sound, if any.             */
  220.     false,            /* Boolean stating if we say stuff.                 */
  221.     {
  222.         0L,            /* Default voice.                                 */
  223.         0L
  224.     },
  225.     nil,            /* Handle to send button control.                 */
  226.     nil,            /* Handle to move notify control.                 */
  227.     nil,            /* Handle to message notify control.             */
  228.     nil,            /* Handle to game-slider control.                 */
  229.     nil,            /* Handle to white-starts radio button.             */
  230.     nil,            /* Handle to black-starts radio button.             */
  231.     nil,            /* Handle to resign button.                         */
  232.     nil,            /* Handle to draw button.                         */
  233.     nil,            /* Handle to record sound button.                 */
  234.     nil,            /* Handle to send sound button.                     */
  235.     0,                /* Above info is reference to controls.             */
  236. }
  237. #endif /* __PPCC__ */
  238. ;
  239.  
  240. /*****************************************************************************/
  241.  
  242. #ifdef __PPCC__
  243.     /*
  244.         Work around compiler bug with aggregate initializers of
  245.         mixed alignment structures (68k structs containing PowerPC
  246.         structs).
  247.     */
  248. void InitNewDocData(void)
  249. {
  250.     int i,j;
  251.     
  252.     newDocData.version = kVersion;
  253.     
  254.     newDocData.printRecValid = false;
  255.     newDocData.print.iPrVersion = 0;
  256.     newDocData.print.prInfo.iDev = 0;
  257.     newDocData.print.prInfo.iVRes = 0;
  258.     newDocData.print.prInfo.iHRes = 0;
  259.     newDocData.print.prInfo.rPage.top = 0;
  260.     newDocData.print.prInfo.rPage.left = 0;
  261.     newDocData.print.prInfo.rPage.bottom = 0;
  262.     newDocData.print.prInfo.rPage.right = 0;
  263.     newDocData.print.rPaper.top = 0;
  264.     newDocData.print.rPaper.left = 0;
  265.     newDocData.print.rPaper.bottom = 0;
  266.     newDocData.print.rPaper.right = 0;
  267.     newDocData.print.prStl.wDev = 0;
  268.     newDocData.print.prStl.iPageV = 0;
  269.     newDocData.print.prStl.iPageH = 0;
  270.     newDocData.print.prStl.bPort = 0;
  271.     newDocData.print.prStl.feed = 0;
  272.     newDocData.print.prInfoPT.iDev = 0;
  273.     newDocData.print.prInfoPT.iVRes = 0;
  274.     newDocData.print.prInfoPT.iHRes = 0;
  275.     newDocData.print.prInfoPT.rPage.top = 0;
  276.     newDocData.print.prInfoPT.rPage.left = 0;
  277.     newDocData.print.prInfoPT.rPage.bottom = 0;
  278.     newDocData.print.prInfoPT.rPage.right = 0;
  279.     newDocData.print.prXInfo.iRowBytes = 0;
  280.     newDocData.print.prXInfo.iBandV = 0;
  281.     newDocData.print.prXInfo.iBandH = 0;
  282.     newDocData.print.prXInfo.iDevBytes = 0;
  283.     newDocData.print.prXInfo.iBands = 0;
  284.     newDocData.print.prXInfo.bPatScale = 0;
  285.     newDocData.print.prXInfo.bUlThick = 0;
  286.     newDocData.print.prXInfo.bUlOffset = 0;
  287.     newDocData.print.prXInfo.bUlShadow = 0;
  288.     newDocData.print.prXInfo.scan = 0;
  289.     newDocData.print.prXInfo.bXInfoX = 0;
  290.     newDocData.print.prJob.iFstPage = 0;
  291.     newDocData.print.prJob.iLstPage = 0;
  292.     newDocData.print.prJob.iCopies = 0;
  293.     newDocData.print.prJob.bJDocLoop = 0;
  294.     newDocData.print.prJob.fFromUsr = 0;
  295.     newDocData.print.prJob.pIdleProc = nil;
  296.     newDocData.print.prJob.pFileName = nil;
  297.     newDocData.print.prJob.iFileVol = 0;
  298.     newDocData.print.prJob.bFileVers = 0;
  299.     newDocData.print.prJob.bJobX = 0;
  300.     for (i=0; i < 19; ++i)
  301.         newDocData.print.printX[i] = 0;
  302.  
  303.     for ( i = 0; i < 120; ++i )
  304.        newDocData.theBoard[i] = OBNDS;
  305.     newDocData.theBoard[21] = BR;
  306.     newDocData.theBoard[22] = BN;
  307.     newDocData.theBoard[23] = BB;
  308.     newDocData.theBoard[24] = BQ;
  309.     newDocData.theBoard[25] = BK;
  310.     newDocData.theBoard[26] = BB;
  311.     newDocData.theBoard[27] = BN;
  312.     newDocData.theBoard[28] = BR;
  313.     newDocData.theBoard[31] = BP;
  314.     newDocData.theBoard[32] = BP;
  315.     newDocData.theBoard[33] = BP;
  316.     newDocData.theBoard[34] = BP;
  317.     newDocData.theBoard[35] = BP;
  318.     newDocData.theBoard[36] = BP;
  319.     newDocData.theBoard[37] = BP;
  320.     newDocData.theBoard[38] = BP;
  321.     for ( i = 40; i <= 70; i += 10 )
  322.        for (j = 1; j<= 8; ++j)
  323.           newDocData.theBoard[i+j] = 0;
  324.     newDocData.theBoard[81] = WP;
  325.     newDocData.theBoard[82] = WP;
  326.     newDocData.theBoard[83] = WP;
  327.     newDocData.theBoard[84] = WP;
  328.     newDocData.theBoard[85] = WP;
  329.     newDocData.theBoard[86] = WP;
  330.     newDocData.theBoard[87] = WP;
  331.     newDocData.theBoard[88] = WP;
  332.     newDocData.theBoard[91] = WR;
  333.     newDocData.theBoard[92] = WN;
  334.     newDocData.theBoard[93] = WB;
  335.     newDocData.theBoard[94] = WQ;
  336.     newDocData.theBoard[95] = WK;
  337.     newDocData.theBoard[96] = WB;
  338.     newDocData.theBoard[97] = WN;
  339.     newDocData.theBoard[98] = WR;
  340.  
  341.     newDocData.king[0].kingLoc = WKPOS;
  342.     newDocData.king[0].kingMoves = 0;
  343.     newDocData.king[0].rookMoves[0] = 0;
  344.     newDocData.king[0].rookMoves[1] = 0;
  345.     newDocData.king[1].kingLoc = BKPOS;
  346.     newDocData.king[1].kingMoves = 0;
  347.     newDocData.king[1].rookMoves[0] = 0;
  348.     newDocData.king[1].rookMoves[1] = 0;
  349.     
  350.     newDocData.enPasMove = 0;
  351.     newDocData.enPasPawnLoc = 0;
  352.     newDocData.arngEnPasMove = 0;
  353.     newDocData.arngEnPasPawnLoc = 0;
  354.     newDocData.numLegalMoves = 0;        
  355.     newDocData.gameIndex = 0;        
  356.     newDocData.numGameMoves = 0;            
  357.     newDocData.myColor = 0;            
  358.     newDocData.startColor = 0;                
  359.     newDocData.arrangeBoard = 0;                
  360.     newDocData.palettePiece = WP;            
  361.     newDocData.defaultTime[0] = 18000L;    
  362.     newDocData.defaultTime[1] = 18000L;        
  363.     newDocData.timeLeft[0] = -1L;        
  364.     newDocData.timeLeft[1] = -1L;            
  365.     newDocData.invertBoard = false;        
  366.     newDocData.endFileInfo1 = 0;
  367.     
  368.     ccpy(newDocData.reconnectZone,"\0");            
  369.     ccpy(newDocData.reconnectMachine,"\0");            
  370.     ccpy(newDocData.reconnectPath,"\0");    
  371.     ccpy(newDocData.reconnectApp,"\0");
  372.     
  373.     newDocData.justBoardWindow = false;
  374.     newDocData.docIsTemplate = false;
  375.     newDocData.keepCMWhite = false;
  376.     newDocData.keepCMBlack = false;
  377.     newDocData.endFileInfo2 = 0;                
  378.  
  379.     newDocData.twoPlayer = false;            
  380.     newDocData.gameID_0 = 0L;                
  381.     newDocData.gameID_1 = 0L;                
  382.     newDocData.sendReason = 0;                
  383.     newDocData.drawBtnState = 0;    
  384.     
  385.     newDocData.locOfOpponent.descriptorType = 0L;
  386.     newDocData.locOfOpponent.dataHandle = nil;
  387.     newDocData.endSendInfo = 0;
  388.     
  389.     newDocData.resync = 0;
  390.     newDocData.creator = false;
  391.     newDocData.displayTime[0] = -1L;            
  392.     newDocData.displayTime[1] = -1L;            
  393.     newDocData.freezeTime[0] = -1L;            
  394.     newDocData.freezeTime[1] = -1L;            
  395.     newDocData.timerRefTick = 0L;
  396.     newDocData.compMoveTick = 0L;
  397.     newDocData.gotUpdateTick = 0L;
  398.     newDocData.compMovesWhite = false;
  399.     newDocData.compMovesBlack = false;
  400.     
  401.     ccpy(newDocData.opponentName,"\0");            
  402.     ccpy(newDocData.opponentZone,"\0");            
  403.     ccpy(newDocData.opponentMachine,"\0");
  404.     
  405.     newDocData.timeLastReceive = 0;
  406.     newDocData.endLocalInfo = 0;
  407.     
  408.     newDocData.configColorChange = false;
  409.     newDocData.configColor = 0;
  410.     newDocData.configTimeChange = false;
  411.     newDocData.configTime[0] = -1L;
  412.     newDocData.configTime[1] = -1L;
  413.     newDocData.endConfigInfo = 0;
  414.     
  415.     newDocData.legalMoves = nil;
  416.     newDocData.gameMoves = nil;
  417.     newDocData.message[0] = nil;
  418.     newDocData.message[1] = nil;
  419.     newDocData.sound = nil;
  420.     newDocData.doSpeech = false;
  421.     newDocData.theVoice.creator = 0L;
  422.     newDocData.theVoice.id = 0L;
  423.     
  424.     newDocData.sendMessage = nil;
  425.     newDocData.beepOnMove = nil;
  426.     newDocData.beepOnMssg = nil;
  427.     newDocData.gameSlider = nil;
  428.     newDocData.wbStart[0] = nil;
  429.     newDocData.wbStart[1] = nil;
  430.     newDocData.resign = nil;
  431.     newDocData.draw = nil;
  432.     newDocData.record = nil;
  433.     newDocData.sendSnd = nil;
  434.     newDocData.endControls = 0;
  435. }
  436. #endif /* __PPCC__ */
  437.  
  438.  
  439. /*****************************************************************************/
  440.  
  441.  
  442.  
  443. #pragma segment Chess
  444. OSErr    InitLogic(void)
  445. {
  446.     short    i;
  447.  
  448.     if (!(gGenMovesHndl = (GameListHndl)NewHandle(0))) return(memFullErr);
  449.  
  450.     for (i = 0; i <= kLastNode; ++i)
  451.         if (!(gNodeHndl[i] = (MoveListHndl)NewHandle(0))) return(memFullErr);
  452.  
  453.     return(noErr);
  454. }
  455.  
  456.  
  457.  
  458. /*****************************************************************************/
  459.  
  460.  
  461.  
  462. #pragma segment Chess
  463. void    NewGame(FileRecHndl game)
  464. {
  465.     TheDocPtr    docPtr;
  466.  
  467.     docPtr = &(*game)->doc;
  468.  
  469.     newDocData.legalMoves = docPtr->legalMoves;
  470.     newDocData.gameMoves  = docPtr->gameMoves;
  471.  
  472.     *docPtr = newDocData;
  473.  
  474.     newDocData.legalMoves = nil;
  475.     newDocData.gameMoves  = nil;
  476. }
  477.  
  478.  
  479.  
  480. /*****************************************************************************/
  481.  
  482.  
  483.  
  484. #pragma segment Chess
  485. void    GenerateLegalMoves(FileRecHndl game)
  486. {
  487.     short            gameIndex, numGameMoves, square, piece;
  488.     short            color, pieceColor;
  489.     short            row, dirNum, dir, dist;
  490.     short            s, d, dest, destColor, epLoc;
  491.     long            size;
  492.     Boolean            docDirty, check;
  493.     GameListHndl    gameMoves;
  494.  
  495.     gameIndex    = (*game)->doc.gameIndex;
  496.     gameMoves    = (*game)->doc.gameMoves;
  497.     numGameMoves = (*game)->doc.numGameMoves;
  498.     docDirty     = (*game)->fileState.docDirty;
  499.  
  500.     (*game)->doc.numLegalMoves = 0;            /* Start the list over. */
  501.  
  502.     if ((gameIndex) && (gameIndex == numGameMoves))
  503.         if (!(**gameMoves)[gameIndex - 1].moveFrom) return;
  504.             /* Resignation or agreed-upon draw recorded.
  505.             ** Game over, so no legal moves. */
  506.  
  507.     SetHandleSize((Handle)gGenMovesHndl, size = GetHandleSize((Handle)gameMoves));
  508.     BlockMove(*(Handle)gameMoves, *(Handle)gGenMovesHndl, size);
  509.     (*game)->doc.gameMoves = gGenMovesHndl;            /* Protect the game moves list. */
  510.  
  511.     color = WhosMove(game);                        /* Who's move it is. */
  512.  
  513.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  514.         /* Scan for pieces of correct color. */
  515.  
  516.         if ((piece = (*game)->doc.theBoard[square]) == EMPTY) continue;
  517.             /* Empty square, so next square, please. */
  518.  
  519.         if (piece == OBNDS) continue;
  520.             /* Out of bounds. */
  521.  
  522.         pieceColor = BLACK;
  523.         if (piece < 0) {
  524.             pieceColor = WHITE;
  525.             piece = -piece;
  526.         }
  527.  
  528.         if (pieceColor != color) continue;
  529.             /* Not our piece. */
  530.  
  531.         row = square / 10;
  532.  
  533.         for (dirNum = 0; (dir = direction[piece][dirNum]) != 0; ++dirNum) {
  534.             /* The direction we will move a piece.  This is correct in all
  535.             ** cases except for white pawns.  Without an adjustment, they
  536.             ** would move in the direction of black pawns, i.e., backwards.
  537.             */
  538.  
  539.             dist = distance[piece];
  540.                 /* The distance a piece can move, in all cases except a
  541.                 ** double-pawn-push and castling.
  542.                 */
  543.  
  544.             if (piece == PAWN) {
  545.                 if (color == WHITE) dir = -dir;
  546.                     /* White pawns will now move forwards. */
  547.  
  548.                 if ((!dirNum) && ((row == 3) || (row == 8))) dist = 2;
  549.                     /* In the case of a pawn, the first direction we check
  550.                     ** is forwards, so if dirNum is 0, we are pushing pawns.
  551.                     **
  552.                     ** Allow double-pawn-push if pawn is on correct row.  We don't
  553.                     ** have to worry about which color the pawn is if the pawn
  554.                     ** has advanced to the other double-push row.  It will
  555.                     ** double-push itself out of bounds.
  556.                     */
  557.             }
  558.  
  559.             for (s = square, d = 1; d <= dist; ++d) {
  560.  
  561.                 s += dir;
  562.                 dest = (*game)->doc.theBoard[s];
  563.                 if (dest == OBNDS) break;    /* Can't go this direction anymore. */
  564.  
  565.                 destColor = BLACK;
  566.                 if (dest < 0) {
  567.                     destColor = WHITE;
  568.                     dest = -dest;
  569.                 }
  570.  
  571.                 if ((dest) && (destColor == color)) break;
  572.                     /* Ran into our own piece, so can't go this direction anymore. */
  573.  
  574.                 if (dest == KING) break;
  575.                     /* Never allow the king to be taken. */
  576.  
  577.                 if (piece == PAWN) {
  578.                     if (!dirNum) {                /* If pawn push... */
  579.                         if (dest) break;        /* Can't take on a pawn-push. */
  580.                     }
  581.                     else {
  582.                         if (dest == EMPTY) {    /* If possible en-passant... */
  583.                             if ((*game)->doc.enPasMove != s) break;
  584.                                 /* Not en-passant pawn capture. */
  585.                             epLoc = (*game)->doc.enPasPawnLoc;
  586.                             dest  = (*game)->doc.theBoard[epLoc];
  587.                             destColor = BLACK;
  588.                             if (dest < 0) {
  589.                                 destColor = WHITE;
  590.                                 dest = -dest;
  591.                             }
  592.                             if (destColor == color) break;
  593.                                 /* We can't en-passant our own piece. */
  594.                             if (dest != PAWN) break;
  595.                                 /* We can only en-passant pawns. */
  596.                         }
  597.                     }
  598.                 }
  599.  
  600.                 MakeMove(game, square, s, QUEEN);
  601.                 check = SquareAttacked(game,
  602.                     (*game)->doc.king[color].kingLoc, color);
  603.                 UnmakeMove(game);
  604.                 if (!check) AddLegalMove(game, square, s);
  605.                     /* Move didn't put (or leave) king in check, so it is a
  606.                     ** valid move.  Since it is valid, record it. */
  607.  
  608.                 if (dest) break;    /* Once we hit a piece, we are
  609.                                     ** done in this direction. */
  610.             }
  611.         }
  612.     }
  613.  
  614.     square = (*game)->doc.king[color].kingLoc;
  615.     if (CastleOkay(game, QSIDE)) AddLegalMove(game, square, square - 2);
  616.     if (CastleOkay(game, KSIDE)) AddLegalMove(game, square, square + 2);
  617.         /* If castling possible, add it to move list. */
  618.  
  619.     (*game)->doc.gameMoves      = gameMoves;
  620.     (*game)->doc.numGameMoves   = numGameMoves;
  621.     (*game)->fileState.docDirty = docDirty;
  622.         /* Restore things the way we were.  We are done with MakeMove services. */
  623. }
  624.  
  625.  
  626.  
  627. /*****************************************************************************/
  628.  
  629.  
  630.  
  631. #pragma segment Chess
  632. void    AddLegalMove(FileRecHndl game, short from, short to)
  633. {
  634.     MoveListHndl    lglMoves;
  635.     short            numLglMoves;
  636.     long            newHndlSize, oldHndlSize;
  637.  
  638.     numLglMoves = (*game)->doc.numLegalMoves;
  639.     lglMoves    = (*game)->doc.legalMoves;
  640.  
  641.     oldHndlSize = GetHandleSize((Handle)lglMoves);
  642.     newHndlSize = ((numLglMoves | 0x3F) + 1) * sizeof(MoveElement);
  643.     if (newHndlSize != oldHndlSize)
  644.         SetHandleSize((Handle)lglMoves, newHndlSize);
  645.  
  646.     (**lglMoves)[numLglMoves].moveFrom = from;
  647.     (**lglMoves)[numLglMoves].moveTo   = to;
  648.     (**lglMoves)[numLglMoves].value    = 0;
  649.  
  650.     ++(*game)->doc.numLegalMoves;
  651. }
  652.  
  653.  
  654.  
  655. /*****************************************************************************/
  656.  
  657.  
  658.  
  659. #pragma segment Chess
  660. Boolean    CastleOkay(FileRecHndl game, short castleSide)
  661. {
  662.     short    color;
  663.     short    castleDir, kingLoc, rookLoc;
  664.     short    i, j, piece, pieceColor;
  665.  
  666.     color = WhosMove(game);            /* Who's move it is. */
  667.  
  668.     if ((*game)->doc.king[color].kingMoves) return(false);
  669.         /* Can't castle.  King has already moved. */
  670.  
  671.     rookLoc = (kingLoc = (*game)->doc.king[color].kingLoc) + 3;
  672.     if ((castleDir = castleSide) == QSIDE) {
  673.         rookLoc -= 7;
  674.         --castleDir;
  675.     }
  676.  
  677.     for (i = kingLoc, j = 3; j; i += castleDir, --j)
  678.         if (SquareAttacked(game, i, color)) return(false);
  679.             /* Can't castle out of, through, or into check. */
  680.  
  681.     if ((*game)->doc.king[color].rookMoves[castleSide]) return(false);
  682.         /* Rook trying to castle with has already moved,
  683.         ** or has been taken.
  684.         */
  685.  
  686.     piece = (*game)->doc.theBoard[rookLoc];
  687.     pieceColor = BLACK;
  688.     if (piece < 0) {
  689.         pieceColor = WHITE;
  690.         piece = -piece;
  691.     }
  692.     if (color != pieceColor) return(false);
  693.     if (piece != ROOK)         return(false);
  694.         /* These deviant conditions can occur if user arranged the board. */
  695.  
  696.     /* So far, everything is cool.  The only remaining possible problem is
  697.     ** that there is a piece (or more) between the king and the rook. */
  698.  
  699.     while (kingLoc += castleDir, kingLoc != rookLoc)
  700.         if ((*game)->doc.theBoard[kingLoc]) return(false);
  701.             /* There is a piece in the way, so we can't castle. */
  702.  
  703.     return(true);        /* The castling move is okay. */
  704. }
  705.  
  706.  
  707.  
  708. /*****************************************************************************/
  709.  
  710.  
  711.  
  712. #pragma segment Chess
  713. void    MakeMove(FileRecHndl game, short moveFrom, short moveTo, short promoteTo)
  714. {
  715.     GameListHndl    gameMoves;
  716.     long            newHndlSize, oldHndlSize;
  717.     short            gameIndex, numGameMoves, color, rank, i;
  718.     short            pieceMoved, pieceCaptured, pieceCapturedFrom;
  719.     short            absPieceMoved, delta, middle, oldRookLoc;
  720.     Boolean            modifyGame;
  721.  
  722.     if (moveFrom == -1) {
  723.         UnmakeMove(game);
  724.         return;
  725.     }
  726.  
  727.     gameIndex    = (*game)->doc.gameIndex;
  728.     numGameMoves = (*game)->doc.numGameMoves;
  729.     gameMoves    = (*game)->doc.gameMoves;
  730.     color        = WhosMove(game);
  731.  
  732.     i = (gameIndex > numGameMoves) ? gameIndex : numGameMoves;
  733.     newHndlSize = ((i | 0x3F) + 1) * sizeof(GameElement);
  734.     oldHndlSize = GetHandleSize((Handle)gameMoves);
  735.     if (newHndlSize != oldHndlSize)
  736.         SetHandleSize((Handle)gameMoves, newHndlSize);
  737.  
  738.     modifyGame = true;
  739.  
  740.     if (moveFrom == 1) {
  741.         if (gameIndex >= numGameMoves) return;
  742.             /* Already positioned at the end of the game. */
  743.         moveFrom   = (**gameMoves)[gameIndex].moveFrom;
  744.         moveTo     = (**gameMoves)[gameIndex].moveTo;
  745.         promoteTo  = (**gameMoves)[gameIndex].promoteTo;
  746.         modifyGame = false;
  747.     }
  748.  
  749.     if (!moveFrom) {        /* Draw agreed upon, or player resigned. */
  750.         if (gameIndex)
  751.             if (!(**gameMoves)[gameIndex - 1].moveFrom) --gameIndex;
  752.                 /* In case there is a race condition, only allow one game-ending
  753.                 ** "move" to occur. */
  754.         (**gameMoves)[gameIndex].moveFrom          = 0;
  755.         (**gameMoves)[gameIndex].moveTo            = moveTo;
  756.         (**gameMoves)[gameIndex].pieceCaptured     = 0;
  757.         (**gameMoves)[gameIndex].pieceCapturedFrom = 0;
  758.         (**gameMoves)[gameIndex].promoteTo         = 0;
  759.         (*game)->doc.gameIndex = ++gameIndex;
  760.         (*game)->doc.numGameMoves   = gameIndex;
  761.         (*game)->fileState.docDirty = true;
  762.         (*game)->doc.resync = kResync;
  763.         return;
  764.     }
  765.  
  766.     pieceMoved        = (*game)->doc.theBoard[moveFrom];
  767.     pieceCaptured     = (*game)->doc.theBoard[moveTo];
  768.     pieceCapturedFrom = moveTo;
  769.  
  770.     absPieceMoved = (pieceMoved < 0) ? -pieceMoved : pieceMoved;
  771.  
  772.     if (absPieceMoved == PAWN) {
  773.  
  774.         rank = moveTo / 10;
  775.         if ((rank == 2) || (rank == 9)) {
  776.             if (promoteTo < 0) promoteTo = -promoteTo;
  777.             pieceMoved *= promoteTo;
  778.             promoteTo = pieceMoved;
  779.         }
  780.         else promoteTo = 0;
  781.  
  782.         if (moveTo == (*game)->doc.enPasMove) {
  783.             pieceCaptured     = -pieceMoved;
  784.             pieceCapturedFrom = (*game)->doc.enPasPawnLoc;
  785.         }        /* If pawn move is onto en-passant move square, then the
  786.                 ** capture is from a square other than moveTo.
  787.                 */
  788.     }
  789.     else promoteTo = 0;
  790.  
  791.     (**gameMoves)[gameIndex].moveFrom          = moveFrom;
  792.     (**gameMoves)[gameIndex].moveTo            = moveTo;
  793.     (**gameMoves)[gameIndex].pieceCaptured     = pieceCaptured;
  794.     (**gameMoves)[gameIndex].pieceCapturedFrom = pieceCapturedFrom;
  795.     (**gameMoves)[gameIndex].promoteTo         = promoteTo;
  796.     (*game)->doc.gameIndex = ++gameIndex;
  797.  
  798.     if (modifyGame) {
  799.         (*game)->doc.numGameMoves   = gameIndex;
  800.         (*game)->fileState.docDirty = true;
  801.     }
  802.  
  803.     /* The move has now been recorded in the move list.  Now make the move. */
  804.  
  805.     (*game)->doc.theBoard[pieceCapturedFrom] = 0;
  806.     (*game)->doc.theBoard[moveTo]            = pieceMoved;
  807.     (*game)->doc.theBoard[moveFrom]          = 0;
  808.         /* The move is now made, except for the rook if castling. */
  809.  
  810.     delta  = moveTo - moveFrom;
  811.     middle = (moveTo + moveFrom) / 2;
  812.  
  813.     if (absPieceMoved == KING) {
  814.         oldRookLoc = 0;
  815.         if (delta == -2) oldRookLoc = moveFrom - 4;
  816.         if (delta == 2)  oldRookLoc = moveFrom + 3;
  817.         if (oldRookLoc) {
  818.             (*game)->doc.theBoard[middle] = (*game)->doc.theBoard[oldRookLoc];
  819.             (*game)->doc.theBoard[oldRookLoc] = 0;
  820.         }
  821.     }        /* Castling (and therefore move) now complete. */
  822.  
  823.  
  824.     /* All that remains is some information updating for castling, king
  825.     ** position, and en-passant. */
  826.  
  827.     if (absPieceMoved == KING) {
  828.         (*game)->doc.king[color].kingLoc = moveTo;
  829.         ++(*game)->doc.king[color].kingMoves;
  830.     }
  831.  
  832.     if ((moveFrom==21) || (moveTo==21)) ++(*game)->doc.king[BLACK].rookMoves[QSIDE];
  833.     if ((moveFrom==28) || (moveTo==28)) ++(*game)->doc.king[BLACK].rookMoves[KSIDE];
  834.     if ((moveFrom==91) || (moveTo==91)) ++(*game)->doc.king[WHITE].rookMoves[QSIDE];
  835.     if ((moveFrom==98) || (moveTo==98)) ++(*game)->doc.king[WHITE].rookMoves[KSIDE];
  836.         /* This accounts for all rook moves/captures, other than castling.
  837.         ** This is necessary to keep track of rook moves/captures to determine
  838.         ** if castling is allowed.  Rook moves when castling don't have to be
  839.         ** accounted for, since the king move in the castle will prevent
  840.         ** any more castling.
  841.         */
  842.  
  843.     /* If the move was a double-pawn-push, then we have some en-passant
  844.     ** information to record.  Otherwise, we need to zero-out these values. */
  845.  
  846.     if (absPieceMoved == PAWN) {
  847.  
  848.         if ((delta != -20) && (delta != 20))
  849.             middle = moveTo = 0;
  850.                 /* If not a double-pawn-push, then record 0's for the
  851.                 ** en-passant information.  This will prevent en-passant
  852.                 ** moves from being generated. */
  853.  
  854.     }
  855.     else middle = moveTo = 0;
  856.  
  857.     (*game)->doc.enPasMove    = middle;
  858.     (*game)->doc.enPasPawnLoc = moveTo;
  859.         /* Record the en-passant information.  These values are
  860.         ** non-zero if a pawn was double-pushed. */
  861.  
  862. }
  863.  
  864.  
  865.  
  866. /*****************************************************************************/
  867.  
  868.  
  869.  
  870. #pragma segment Chess
  871. void    UnmakeMove(FileRecHndl game)
  872. {
  873.     GameListHndl    gameMoves;
  874.     short            gameIndex, numGameMoves;
  875.     short            moveFrom, moveTo, pieceCaptured, pieceCapturedFrom;
  876.     short            promoteTo, pieceMoved, color, delta, oldRookLoc, middle;
  877.  
  878.     gameIndex    = (*game)->doc.gameIndex;
  879.     numGameMoves = (*game)->doc.numGameMoves;
  880.     gameMoves    = (*game)->doc.gameMoves;
  881.  
  882.     if (!gameIndex) return;
  883.     --gameIndex;
  884.  
  885.     moveFrom          = (**gameMoves)[gameIndex].moveFrom;
  886.     moveTo            = (**gameMoves)[gameIndex].moveTo;
  887.     pieceCaptured     = (**gameMoves)[gameIndex].pieceCaptured;
  888.     pieceCapturedFrom = (**gameMoves)[gameIndex].pieceCapturedFrom;
  889.     promoteTo         = (**gameMoves)[gameIndex].promoteTo;
  890.  
  891.     if (moveFrom) {        /* The "move" could be a resign or draw.  Make sure it isn't. */
  892.         pieceMoved = (*game)->doc.theBoard[moveTo];
  893.         if (promoteTo) pieceMoved = (pieceMoved < 0) ? -1 : 1;
  894.  
  895.         (*game)->doc.theBoard[moveFrom]          = pieceMoved;
  896.         (*game)->doc.theBoard[moveTo]            = 0;
  897.         (*game)->doc.theBoard[pieceCapturedFrom] = pieceCaptured;
  898.             /* Any move now undone, except for castling.  The rook still has
  899.             ** to be put back.
  900.             */
  901.  
  902.         if ((pieceMoved == KING) || (pieceMoved == -KING)) {
  903.  
  904.             color = ((gameIndex + (*game)->doc.startColor) & 0x01);        /* Who's move it is. */
  905.             (*game)->doc.king[color].kingLoc = moveFrom;
  906.             --(*game)->doc.king[color].kingMoves;
  907.  
  908.             delta  = moveTo - moveFrom;
  909.             oldRookLoc = 0;
  910.             if (delta == -2) oldRookLoc = moveFrom - 4;
  911.             if (delta == 2)  oldRookLoc = moveFrom + 3;
  912.             if (oldRookLoc) {
  913.                 middle = (moveTo + moveFrom) / 2;
  914.                 (*game)->doc.theBoard[oldRookLoc] = (*game)->doc.theBoard[middle];
  915.                 (*game)->doc.theBoard[middle] = 0;
  916.             }
  917.         }        /* Castling now completely undone. */
  918.  
  919.         if ((moveFrom == 21) || (moveTo == 21)) --(*game)->doc.king[BLACK].rookMoves[QSIDE];
  920.         if ((moveFrom == 28) || (moveTo == 28)) --(*game)->doc.king[BLACK].rookMoves[KSIDE];
  921.         if ((moveFrom == 91) || (moveTo == 91)) --(*game)->doc.king[WHITE].rookMoves[QSIDE];
  922.         if ((moveFrom == 98) || (moveTo == 98)) --(*game)->doc.king[WHITE].rookMoves[KSIDE];
  923.             /* Undo any rook move/capture accounting.  This info is used when
  924.             ** determining of castling is allowed.
  925.             */
  926.  
  927.         (*game)->doc.enPasMove = (*game)->doc.enPasPawnLoc = 0;
  928.             /* Assume move previous to the one we just undid was not a
  929.             ** double-pawn-push.  If it was not a double-pawn-push, then
  930.             ** en-passant moves are not possible at the move number we
  931.             ** just undid.
  932.             */
  933.     }
  934.  
  935.     if (gameIndex) {        /* Restore en-passant possibilities. */
  936.  
  937.         moveFrom   = (**gameMoves)[--gameIndex].moveFrom;
  938.         moveTo     = (**gameMoves)[gameIndex++].moveTo;
  939.         pieceMoved = (*game)->doc.theBoard[moveTo];
  940.  
  941.         if ((pieceMoved == PAWN) || (pieceMoved == -PAWN)) {
  942.             delta = moveTo - moveFrom;
  943.             if ((delta == -20) || (delta == 20)) {
  944.                 (*game)->doc.enPasMove    = (moveTo + moveFrom) / 2;
  945.                 (*game)->doc.enPasPawnLoc = moveTo;
  946.             }
  947.         }
  948.     }
  949.     else {
  950.         (*game)->doc.enPasMove    = (*game)->doc.arngEnPasMove;
  951.         (*game)->doc.enPasPawnLoc = (*game)->doc.arngEnPasPawnLoc;
  952.     }
  953.  
  954.     (*game)->doc.gameIndex = gameIndex;
  955. }
  956.  
  957.  
  958.  
  959.  
  960. /*****************************************************************************/
  961.  
  962.  
  963.  
  964. #pragma segment Chess
  965. short    SquareAttacked(FileRecHndl game, short square, short color)
  966. {
  967.     short    destColor;
  968.     short    kind;
  969.     short    dirNum, dir;
  970.     short    s, dist, maxDist, dest, taker, takerLoc;
  971.  
  972.     taker    = KING + 1;        /* This is a no-take flag. */
  973.     takerLoc = 0;
  974.  
  975.     for (kind = KNIGHT; kind <= QUEEN; kind += (QUEEN - KNIGHT)) {
  976.         /* Check in the knight and queen directions. */
  977.  
  978.         for (dirNum = 0; (dir = direction[kind][dirNum]) != 0; ++dirNum) {
  979.             /* The direction we will scan for an attack, for the most part. */
  980.  
  981.             maxDist = (kind == KNIGHT) ? 1 : 7;
  982.             for (s = square, dist = 1; dist <= maxDist; ++dist) {
  983.  
  984.                 s += dir;
  985.                 if ((dest = (*game)->doc.theBoard[s]) == EMPTY) continue;
  986.                     /* Empty square, so keep looking. */
  987.  
  988.                 if (dest == OBNDS) break;
  989.                     /* Can't be attacked from this direction anymore. */
  990.  
  991.                 destColor = BLACK;
  992.                 if (dest < 0) {
  993.                     destColor = WHITE;
  994.                     dest = -dest;
  995.                 }
  996.  
  997.                 if (destColor == color) break;
  998.                     /* Ran into our own piece, so no attack
  999.                     ** from this direction.
  1000.                     */
  1001.  
  1002.                 if (dest >= taker) continue;
  1003.  
  1004.                 if (kind == KNIGHT)    {        /* If we are looking for knights... */
  1005.                     if (dest == KNIGHT) {
  1006.                         taker    = KNIGHT;
  1007.                         takerLoc = s;
  1008.                         dirNum   = 7;        /* Since there is 'an' attack by a knight,
  1009.                                             ** we don't care if there is another.
  1010.                                             ** We have already established a knight
  1011.                                             ** take, so we can skip the rest of the
  1012.                                             ** knight directions. */
  1013.                     }
  1014.                     continue;        /* Only knights can take in this direction. */
  1015.                 }
  1016.  
  1017.                 if (dest == KING) {
  1018.                     if (dist == 1) {
  1019.                         taker    = KING;
  1020.                         takerLoc = s;
  1021.                     }
  1022.                     break;
  1023.                 }            /* We are looking in the non-knight move directions
  1024.                             ** for attackers.  If a king is found as the possible
  1025.                             ** attacker, make sure it is in range (one square away)
  1026.                             ** before counting it as an attack.  If it isn't one
  1027.                             ** square away, then it serves to prevent any other
  1028.                             ** attack from this direction.
  1029.                             */
  1030.  
  1031.                 if (dest == QUEEN) {
  1032.                     taker    = QUEEN;
  1033.                     takerLoc = s;
  1034.                     break;
  1035.                 }            /* If the potential attacker is a queen, then it is
  1036.                             ** a valid attacker.
  1037.                             */
  1038.  
  1039.                 if (dest == ROOK) {
  1040.                     if (dirNum > 3) {
  1041.                         taker    = ROOK;
  1042.                         takerLoc = s;
  1043.                     }
  1044.                     break;
  1045.                 }            /* If the potential attacker is a rook, and we are
  1046.                             ** examining a rank or file, then count it as an attacker.
  1047.                             ** Otherwise, the rook serves to prevent any other
  1048.                             ** attack from this direction.
  1049.                             */
  1050.  
  1051.                 if (dest == BISHOP) {
  1052.                     if (dirNum < 4) {
  1053.                         taker    = BISHOP;
  1054.                         takerLoc = s;
  1055.                     }
  1056.                     break;
  1057.                 }            /* If the potential attacker is a bishop, and we are
  1058.                             ** examining a diagonal, then count it as an attacker.
  1059.                             ** Otherwise, the bishop serves to prevent any other
  1060.                             ** attack from this direction.
  1061.                             */
  1062.  
  1063.                 if (dest == PAWN) {
  1064.                     if (destColor == BLACK) {
  1065.                         if ((dirNum < 2) && (dist == 1)) {
  1066.                             taker    = PAWN;
  1067.                             takerLoc = s;
  1068.                         }
  1069.                     }
  1070.                     else
  1071.                         if (
  1072.                             (dirNum > 1) && 
  1073.                             (dirNum < 4) && 
  1074.                             (dist == 1)
  1075.                         ) {
  1076.                             taker    = PAWN;
  1077.                             takerLoc = s;
  1078.                         }
  1079.                     break;
  1080.                 }
  1081.  
  1082.                 break;
  1083.                     /* Final case is a knight in a non-knight direction. */
  1084.             }
  1085.         }
  1086.     }
  1087.  
  1088.     return(takerLoc);
  1089. }
  1090.  
  1091.  
  1092.  
  1093. /*****************************************************************************/
  1094.  
  1095.  
  1096.  
  1097. #pragma segment Chess
  1098. void    EndTheGame(FileRecHndl game, short endReason)
  1099. {
  1100.     WindowPtr    oldPort;
  1101.  
  1102.     MakeMove(game, 0, endReason, 0);
  1103.         /* Record who resigned or draw agreement. */
  1104.  
  1105.     oldPort = SetFilePort(game);
  1106.     ImageDocument(game, true);
  1107.     AdjustGameSlider(game);
  1108.     UpdateGameStatus(game);
  1109.     SetPort(oldPort);
  1110. }
  1111.  
  1112.  
  1113.  
  1114. /*****************************************************************************/
  1115.  
  1116.  
  1117.  
  1118. #pragma segment Chess
  1119. short    WhosMove(FileRecHndl game)
  1120. {
  1121.     short    color;
  1122.  
  1123.     color  = ((*game)->doc.gameIndex ^ (*game)->doc.startColor);
  1124.     return(color & 0x01);
  1125. }
  1126.  
  1127.  
  1128.  
  1129. /*****************************************************************************/
  1130.  
  1131.  
  1132.  
  1133. #pragma segment Chess
  1134. short    GameStatus(FileRecHndl game)
  1135. {
  1136.     short            i, color, kingLoc;
  1137.     short            board[120], *boardPtr;
  1138.     short            origGameIndex, gameIndex;
  1139.     short            rep, back, pieceMoved, gameStat;
  1140.     GameListHndl    gameMoves;
  1141.  
  1142.     gPosReps = 0;
  1143.  
  1144.     if ((*game)->doc.arrangeBoard) return(kGameContinues);
  1145.  
  1146.     for (i = 0; i < 2; ++i)
  1147.         if (!(*game)->doc.timeLeft[i])
  1148.             return(kYouLoseOnTime - (i ^ (*game)->doc.myColor));
  1149.  
  1150.     GenerateLegalMoves(game);
  1151.  
  1152.     gameMoves     = (*game)->doc.gameMoves;
  1153.     origGameIndex = (*game)->doc.gameIndex;
  1154.  
  1155.     if (!(*game)->doc.numLegalMoves) {
  1156.  
  1157.         color = WhosMove(game);            /* Who's move it is. */
  1158.  
  1159.         if ((origGameIndex) && (origGameIndex == (*game)->doc.numGameMoves))
  1160.             if (!(**gameMoves)[origGameIndex - 1].moveFrom)
  1161.                 return((**gameMoves)[origGameIndex - 1].moveTo);
  1162.  
  1163.         kingLoc = (*game)->doc.king[color].kingLoc;
  1164.  
  1165.         if (SquareAttacked(game, kingLoc, color)) {        /* Checkmated. */
  1166.             if (color == (*game)->doc.myColor) return(kYouLose);
  1167.             else return(kYouWin);
  1168.         }
  1169.  
  1170.         return(kStalemate);
  1171.     }
  1172.  
  1173.     BlockMove((Ptr)&(*game)->doc.theBoard[0], (Ptr)&board[0], 120 * sizeof(short));
  1174.  
  1175.     rep = 2;            /* 2 matching current position makes 3 that match. */
  1176.     back = 100;
  1177.  
  1178.     gameIndex = (*game)->doc.gameIndex;
  1179.     while ((rep) && (back) && (gameIndex)) {
  1180.  
  1181.         if ((**gameMoves)[--gameIndex].pieceCaptured) break;
  1182.  
  1183.         pieceMoved = (*game)->doc.theBoard[(**gameMoves)[gameIndex].moveTo];
  1184.         if (pieceMoved < 0) pieceMoved = -pieceMoved;
  1185.         if (pieceMoved == PAWN) break;
  1186.  
  1187.         UnmakeMove(game);
  1188.         --back;
  1189.  
  1190.         boardPtr = &(*game)->doc.theBoard[0];
  1191.         for (i = START_IBNDS; i < END_IBNDS; ++i) if (boardPtr[i] != board[i]) break;
  1192.  
  1193.         if (i == END_IBNDS) {
  1194.             ++gPosReps;
  1195.             if (!(--rep)) break;
  1196.         }
  1197.     }
  1198.  
  1199.     while ((*game)->doc.gameIndex != origGameIndex) MakeMove(game, 1, 0, 0);
  1200.  
  1201.     gameStat = kGameContinues;
  1202.     if (!rep)  gameStat = kDrawByRep;
  1203.     if (!back) gameStat = kDrawBy50;
  1204.  
  1205.     if (gameStat) (*game)->doc.numLegalMoves = 0;
  1206.  
  1207.     return(gameStat);
  1208. }
  1209.  
  1210.  
  1211.  
  1212. /*****************************************************************************/
  1213.  
  1214.  
  1215.  
  1216. #pragma segment Chess
  1217. short    UpdateTime(FileRecHndl game, Boolean canLose)
  1218. {
  1219.     FileRecPtr    frPtr;
  1220.     short        moveColor, myColor;
  1221.     long        timeLeft, opponentTimeLeft, oldTimeLeft, diff;
  1222.  
  1223.     frPtr = *game;
  1224.     moveColor        = WhosMove(game);
  1225.     myColor          = frPtr->doc.myColor;
  1226.     timeLeft         = frPtr->doc.timeLeft[moveColor];
  1227.     opponentTimeLeft = frPtr->doc.timeLeft[moveColor ^ 1];
  1228.  
  1229.     if (!frPtr->doc.twoPlayer) myColor = moveColor;
  1230.         /* Since we are not playing over the net, both sides can lose 
  1231.         ** due to time on this machine. */
  1232.  
  1233.     if ((timeLeft > 0) && (opponentTimeLeft > 0)) {
  1234.         oldTimeLeft = timeLeft;
  1235.         diff = TickCount() - frPtr->doc.timerRefTick;
  1236.         if (diff < 0) {
  1237.             frPtr->doc.timerRefTick = TickCount();
  1238.             diff = 0;
  1239.         }
  1240.         diff /= 60;
  1241.         diff *= 60;
  1242.         if (diff >= 60) {
  1243.             if (!GameStatus(game)) {
  1244.                 timeLeft -= diff;
  1245.                 if (timeLeft < 60) timeLeft = 0;
  1246.                 if (!timeLeft)
  1247.                     if ((myColor != moveColor) || (!canLose)) timeLeft = 60;
  1248.                 frPtr->doc.timeLeft[moveColor] = timeLeft;
  1249.                 frPtr->doc.timerRefTick += diff;
  1250.                 if (!timeLeft) return(2);
  1251.                 if (timeLeft != oldTimeLeft) return(1);
  1252.             }
  1253.             else (*game)->doc.timerRefTick = TickCount();
  1254.                 /* Someone has already lost, so no time change. */
  1255.         }
  1256.     }
  1257.     else (*game)->doc.timerRefTick = TickCount();
  1258.  
  1259.     return(0);
  1260. }
  1261.  
  1262.  
  1263.  
  1264. /*****************************************************************************/
  1265.  
  1266.  
  1267.  
  1268. #pragma segment Chess
  1269. void    UpdateGameStatus(FileRecHndl game)
  1270. {
  1271.     WindowPtr        oldPort;
  1272.     ControlHandle    draw, resign;
  1273.     short            status, myColor;
  1274.     Rect            drawRect, resignRect, workRect;
  1275.     Point            endOfText;
  1276.     Boolean            hideEm, hidden;
  1277.     Str255            reasonText;
  1278.  
  1279.     if ((*game)->doc.arrangeBoard) return;
  1280.  
  1281.     draw   = (*game)->doc.draw;
  1282.     resign = (*game)->doc.resign;
  1283.  
  1284.     if (!draw) return;
  1285.  
  1286.     oldPort = SetFilePort(game);
  1287.  
  1288.     drawRect   = (*draw)->contrlRect;
  1289.     resignRect = (*resign)->contrlRect;
  1290.  
  1291.     hideEm = false;
  1292.     status = GameStatus(game);
  1293.     if (status) hideEm = true;
  1294.  
  1295.     hidden = false;
  1296.     if (drawRect.top & 0x4000) hidden = true;
  1297.  
  1298.     workRect = drawRect;
  1299.     if (hidden) OffsetRect(&workRect, 0, -0x4000);
  1300.     workRect.right = resignRect.right;
  1301.  
  1302.     if (hideEm != hidden) {
  1303.         EraseRect(&workRect);
  1304.         MoveControl(draw,   drawRect.left,   drawRect.top ^ 0x4000);
  1305.         MoveControl(resign, resignRect.left, resignRect.top ^ 0x4000);
  1306.     }
  1307.  
  1308.     if (hideEm) {
  1309.         myColor = (*game)->doc.myColor;
  1310.         switch (status) {
  1311.             case kYouWin:
  1312.             case kYouWinOnTime:
  1313.                 status = kWhiteWins + myColor;
  1314.                 break;
  1315.             case kYouLose:
  1316.             case kYouLoseOnTime:
  1317.                 status = kBlackWins - myColor;
  1318.                 break;
  1319.         }
  1320.  
  1321.         TextMode(srcCopy);
  1322.         TextFont(systemFont);
  1323.         TextSize(0);
  1324.         GetIndString(reasonText, rGameStat, status);
  1325.         MoveTo(workRect.left, workRect.top + 14);
  1326.         DrawString(reasonText);
  1327.         TextMode(srcOr);
  1328.         GetPen(&endOfText);
  1329.         workRect.left = endOfText.h;
  1330.         EraseRect(&workRect);
  1331.     }
  1332.  
  1333.     SetPort(oldPort);
  1334. }
  1335.  
  1336.  
  1337.  
  1338. /*****************************************************************************/
  1339.  
  1340.  
  1341.  
  1342. #pragma segment Chess
  1343. void    DrawButtonTitle(FileRecHndl game, short newVal)
  1344. {
  1345.     WindowPtr        oldPort;
  1346.     ControlHandle    draw;
  1347.     Rect            drawRect;
  1348.     Str255            drawButtonText;
  1349.  
  1350.     if (newVal != (*game)->doc.drawBtnState) {
  1351.  
  1352.         oldPort = SetFilePort(game);
  1353.  
  1354.         GetIndString(drawButtonText, rGameStat, kDrawButtonText + newVal);
  1355.  
  1356.         draw = (*game)->doc.draw;
  1357.         SetControlTitle(draw, drawButtonText);
  1358.         (*game)->doc.drawBtnState = newVal;
  1359.  
  1360.         drawRect = (*draw)->contrlRect;
  1361.         ValidRect(&drawRect);
  1362.  
  1363.         SetPort(oldPort);
  1364.     }
  1365. }
  1366.  
  1367.  
  1368.  
  1369. /*****************************************************************************/
  1370. /*****************************************************************************/
  1371.  
  1372.  
  1373.  
  1374. #pragma segment Chess
  1375. Boolean    ComputerMove(FileRecHndl game)
  1376. {
  1377.     OSErr            err;
  1378.     FileRecHndl        workGame;
  1379.     short            numLegalMoves, moveNum, fromSq, toSq, i;
  1380.     short            wtotal, btotal, color, update;
  1381.     WindowPtr        window;
  1382.     Boolean            compMoved;
  1383.     MoveListHndl    legalMoves;
  1384.  
  1385.     compMoved = false;
  1386.  
  1387.     IncNewFileNum(false);
  1388.     err = AppDuplicateDocument(game, &workGame);
  1389.     IncNewFileNum(true);
  1390.     if (err) return(false);
  1391.  
  1392.     for (i = 0; i < 2; ++i) (*workGame)->doc.timeLeft[i] = (*game)->doc.timeLeft[i];
  1393.  
  1394.     GetPort(&window);
  1395.     (*workGame)->fileState.window = window;
  1396.  
  1397.     GenerateLegalMoves(game);
  1398.     GenerateLegalMoves(workGame);
  1399.  
  1400.     numLegalMoves = (*workGame)->doc.numLegalMoves;
  1401.     legalMoves    = (*workGame)->doc.legalMoves;
  1402.  
  1403.     if (numLegalMoves) {        /* If there is a move, pick one, any one. */
  1404.         if (numLegalMoves == 1) moveNum = 0;
  1405.         else {
  1406.             moveNum = CheckForMate(workGame, 0, 2);
  1407.             if (moveNum == -1) {
  1408.                 CalcPositionValues(workGame);
  1409.                 wtotal = gWhiteTotal >> 16;
  1410.                 btotal = gBlackTotal >> 16;
  1411.                 color  = WhosMove(workGame);
  1412.                 for (;;) {
  1413.                     if (gNumPieces == 1) {
  1414.                         if (
  1415.                             ((color == WHITE) && (wtotal == 9)) ||
  1416.                             ((color == BLACK) && (btotal == 9))
  1417.                         ) {
  1418.                             moveNum = QueenMate(workGame);
  1419.                             break;
  1420.                         }
  1421.                         if (
  1422.                             ((color == WHITE) && (wtotal == 5)) ||
  1423.                             ((color == BLACK) && (btotal == 5))
  1424.                         ) {
  1425.                             moveNum = RookMate(workGame);
  1426.                             break;
  1427.                         }
  1428.                     }
  1429.                     moveNum = BestMove(workGame);
  1430.                     break;
  1431.                 }
  1432.             }
  1433.         }
  1434.  
  1435.         if (moveNum > -1) {
  1436.             update = UpdateTime(game, true);
  1437.             if (update) DrawTime(game);
  1438.             if (update == 2) {
  1439.                 if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1440.                 AlertIfGameOver(game);
  1441.             }
  1442.             else {
  1443.                 fromSq = (**legalMoves)[moveNum].moveFrom;
  1444.                 toSq   = (**legalMoves)[moveNum].moveTo;
  1445.                 SlideThePiece(game, fromSq, toSq);
  1446.                 MakeMove(game, fromSq, toSq, QUEEN);
  1447.                 compMoved = true;
  1448.             }
  1449.         }
  1450.         if (moveNum == kComputerResigns) {
  1451.             EndTheGame(game, kWhiteResigns + WhosMove(game));
  1452.             if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1453.             gComputerResigns = true;
  1454.             AlertIfGameOver(game);
  1455.         }
  1456.     }
  1457.  
  1458.     AppDisposeDocument(workGame);
  1459.     return(compMoved);
  1460. }
  1461.  
  1462.  
  1463.  
  1464. /*****************************************************************************/
  1465.  
  1466.  
  1467.  
  1468. #pragma segment Chess
  1469. short    CheckForMate(FileRecHndl game, short nodeDepth, short maxDepth)
  1470. {
  1471.     short            num, ourMove, move;
  1472.     short            color, kingLoc, result;
  1473.     MoveListHndl    node;
  1474.     EventRecord        event;
  1475.  
  1476.     ourMove = -1;
  1477.     GenerateLegalMoves(game);
  1478.  
  1479.     num  = (*game)->doc.numLegalMoves;
  1480.     node = (*game)->doc.legalMoves;
  1481.  
  1482.     (*game)->doc.legalMoves = gNodeHndl[nodeDepth];
  1483.         /* Protect the list of legal moves for this level.  Put a handle
  1484.         ** into the game where moves for the next level can be placed. */
  1485.  
  1486.     color = WhosMove(game) ^ 1;
  1487.     if (!(nodeDepth & 0x01)) {
  1488.         for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1489.             MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1490.             kingLoc = (*game)->doc.king[color].kingLoc;
  1491.             if (SquareAttacked(game, kingLoc, color)) {        /* The move caused check. */
  1492.                 GenerateLegalMoves(game);
  1493.                 if (!(*game)->doc.numLegalMoves) {            /* If no way out of check... */
  1494.                     ourMove = move;                            /* ...it is checkmate. */
  1495.                 }
  1496.             }
  1497.             UnmakeMove(game);
  1498.         }
  1499.         if (idleTick + 10 < TickCount()) {
  1500.             idleTick = TickCount();
  1501.             if (EventAvail(everyEvent - highLevelEventMask, &event)) ourMove = -2;
  1502.             else {
  1503.                 CTEIdle();
  1504.                 DoIdleTasks(false);
  1505.             }
  1506.         }
  1507.     }
  1508.  
  1509.     if (nodeDepth < maxDepth) {
  1510.         if (ourMove == -1) {
  1511.             for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1512.                 MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1513.                 result = CheckForMate(game, nodeDepth + 1, maxDepth);
  1514.                 if (result == -1) {
  1515.                     ourMove = move;
  1516.                 }
  1517.                     /* If opponent can't find saving move, then this is the move we want. */
  1518.                 if (result == -2) ourMove = -2;
  1519.                     /* User interrupted search. */
  1520.                 UnmakeMove(game);
  1521.             }
  1522.         }
  1523.     }
  1524.  
  1525.     if (!nodeDepth) {
  1526.         if (ourMove > -1) {
  1527.             MakeMove(game, (**node)[ourMove].moveFrom, (**node)[ourMove].moveTo, QUEEN);
  1528.             kingLoc = (*game)->doc.king[color].kingLoc;
  1529.             if (!SquareAttacked(game, kingLoc, color)) {    /* It is not a mate in 1. */
  1530.                 GenerateLegalMoves(game);
  1531.                 if (!(*game)->doc.numLegalMoves) ourMove = -1;
  1532.                     /* It is a stalemate.  Throw it back. */
  1533.             }
  1534.             UnmakeMove(game);
  1535.         }
  1536.     }
  1537.  
  1538.     (*game)->doc.numLegalMoves = num;
  1539.     (*game)->doc.legalMoves    = node;
  1540.  
  1541.     return(ourMove);
  1542. }
  1543.  
  1544.  
  1545.  
  1546. /*****************************************************************************/
  1547.  
  1548.  
  1549.  
  1550. #pragma segment Chess
  1551. short    BestMove(FileRecHndl game)
  1552. {
  1553.     short            bestMove, keepBestMove, num;
  1554.     short            i, color, move, from, to;
  1555.     Boolean            playOn;
  1556.     long            value, max;
  1557.     MoveListHndl    node;
  1558.     EventRecord        event;
  1559.  
  1560.     bestMove = -1;
  1561.  
  1562.     num  = (*game)->doc.numLegalMoves;
  1563.     node = (*game)->doc.legalMoves;
  1564.  
  1565.     (*game)->doc.legalMoves = gNodeHndl[kLastNode];
  1566.         /* Protect the list of legal moves for this node.  Put a different handle
  1567.         ** into the game where moves for the next level can be placed. */
  1568.  
  1569.     color  = WhosMove(game);
  1570.     playOn = false;
  1571.     if ((i = (*game)->doc.timeLeft[1 - color]) >= 0)
  1572.         if (i < 7200)
  1573.             playOn = true;
  1574.  
  1575.     for (max = 0x80000000L, move = 0; move < num; ++move) {
  1576.         from = (**node)[move].moveFrom;
  1577.         to   = (**node)[move].moveTo;
  1578.         (**node)[move].value = value = OneDeepEval(game, from, to, color);
  1579.         if (max < value) {
  1580.             max = value;
  1581.             bestMove = move;        /* Best move so far. */
  1582.         }
  1583.         if (idleTick + 10 < TickCount()) {
  1584.             idleTick = TickCount();
  1585.             if (EventAvail(everyEvent - highLevelEventMask, &event)) {
  1586.                 bestMove = -2;
  1587.                 break;
  1588.             }
  1589.             CTEIdle();
  1590.             DoIdleTasks(false);
  1591.         }
  1592.     }
  1593.  
  1594.     if (bestMove > -1) {        /* Make sure we aren't getting mated in two. */
  1595.         for (keepBestMove = bestMove;;) {
  1596.             from = (**node)[bestMove].moveFrom;
  1597.             to   = (**node)[bestMove].moveTo;
  1598.             MakeMove(game, from, to, QUEEN);
  1599.             i = CheckForMate(game, 0, 2);    /* Check for mate in 2. */
  1600.             UnmakeMove(game);
  1601.             if (i == -2) {
  1602.                 bestMove = -2;
  1603.                 break;        /* User wants to do something, so interrupt. */
  1604.             }
  1605.             if (i == -1) {
  1606.                 max = (**node)[bestMove].value;
  1607.                 if (!playOn)
  1608.                     if (max < -0x00070000)
  1609.                         if (!(Random() & 0x03))
  1610.                             bestMove = kComputerResigns;    /* Resign sometimes. */
  1611.                 break;        /* Opponent has no mate in 2 against bestMove. */
  1612.             }
  1613.             (**node)[bestMove].value = max = 0x80000000L;    /* Getting mated is bad-bad. */
  1614.             for (i = 0; i < num; ++i) {        /* Try the next best move. */
  1615.                 if (max < (**node)[i].value) {
  1616.                     max = (**node)[i].value;
  1617.                     bestMove = i;
  1618.                 }
  1619.             }
  1620.             if (max == 0x80000000L) {
  1621.                 bestMove = keepBestMove;    /* We are going to get mated.  (Bummer.) */
  1622.                 if (!playOn)
  1623.                     if (!(Random() & 0x01))
  1624.                         bestMove = kComputerResigns;    /* Resign sometimes. */
  1625.                 break;
  1626.             }
  1627.         }
  1628.     }
  1629.  
  1630.     (*game)->doc.numLegalMoves = num;
  1631.     (*game)->doc.legalMoves    = node;
  1632.  
  1633.     return(bestMove);
  1634. }
  1635.  
  1636.  
  1637.  
  1638. /*****************************************************************************/
  1639.  
  1640.  
  1641.  
  1642. #pragma segment Chess
  1643. long    OneDeepEval(FileRecHndl game, short from, short to, short color)
  1644. {
  1645.     short            material, matBalance, xcngVal, posVal;
  1646.     short            take, takerLoc, retakerLoc;
  1647.     short            val, saveBoard;
  1648.     short            *boardPtr, keepBoard[120], square, j, k, xcolor;
  1649.     short            movedPiece, piece, pieceColor, r, c, cc, delta, xcng[64], xnum, loop;
  1650.     short            dirNum, dir, s, dist, numChecks;
  1651.     long            value;
  1652.  
  1653.     MakeMove(game, from, to, QUEEN);
  1654.  
  1655.     boardPtr   = &(*game)->doc.theBoard[0];
  1656.     matBalance = xcngVal = posVal = 0;
  1657.  
  1658.     for (material = 0, square = START_IBNDS; square < END_IBNDS; ++square) {
  1659.         piece = boardPtr[square];
  1660.         if (piece) {
  1661.             if (piece != OBNDS) {
  1662.                 if (piece < 0) piece = -piece;
  1663.                 if (piece == KING)   piece = 0;
  1664.                 if (piece == QUEEN)  piece = 9;
  1665.                 if (piece == ROOK)   piece = 5;
  1666.                 if (piece == KNIGHT) piece = 3;
  1667.                 material += piece;
  1668.             }
  1669.         }
  1670.     }
  1671.  
  1672.     movedPiece = boardPtr[to];
  1673.     if (movedPiece < 0) movedPiece = -movedPiece;
  1674.  
  1675.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  1676.  
  1677.         piece = boardPtr[square];
  1678.         if (piece) {        /* Evaluate value of this piece being here. */
  1679.  
  1680.             if ((piece) && (piece != OBNDS)) {
  1681.  
  1682.                 pieceColor = BLACK;    /* Figure who's piece it is. */
  1683.                 if (piece < 0) {
  1684.                     pieceColor = WHITE;
  1685.                     piece = -piece;
  1686.                 }
  1687.  
  1688.                 j = piece;
  1689.                 if (j == QUEEN)  j = 9;
  1690.                 if (j == ROOK)   j = 5;
  1691.                 if (j == KNIGHT) j = 3;
  1692.                 if (color == pieceColor) matBalance += j;
  1693.                 else                     matBalance -= j;
  1694.  
  1695.                 r = square / 10;    /* Get row and column of piece. */
  1696.                 c = square - 10 * r - 1;
  1697.                 r -= 2;
  1698.  
  1699.                 if (pieceColor == WHITE) r = 7 - r;        /* Flip rank for white. */
  1700.  
  1701.                 if (piece == PAWN) {            /* Weight the pawn position. */
  1702.                     if (r > 4) {
  1703.                         if (r == 6) r = 63;        /* Highly advanced pawns are nice. */
  1704.                         if (r == 5) r = 30;
  1705.                     }
  1706.                     else {
  1707.                         if (material > 40) {
  1708.                             cc = c;
  1709.                             if (cc > 3) cc = 7 - cc;
  1710.                             if (r < 2) cc = 0;
  1711.                             switch (cc) {
  1712.                                 case 0:
  1713.                                     r = 0;
  1714.                                     break;
  1715.                                 case 1:
  1716.                                     if (r > 2) r = -8;
  1717.                                     else       r = 0;
  1718.                                     break;
  1719.                                 case 2:
  1720.                                     if (r == 2) r = -2;
  1721.                                     if (r == 3) {
  1722.                                         r = 6;
  1723.                                         if (c < 5) r = 11;
  1724.                                     }
  1725.                                     break;
  1726.                                 case 3:
  1727.                                     if (r == 2) r = 6;
  1728.                                     if (r == 3) {
  1729.                                         r = 6;
  1730.                                         if (c < 5) r = 11;
  1731.                                     }
  1732.                                     break;
  1733.                             }
  1734.                         }
  1735.                         if (material < 26) r *= 4;
  1736.                     }
  1737.                     for (j = 9; j <= 11; j += 2) {    /* Give weight to pawn chains. */
  1738.                         k = boardPtr[square + j];
  1739.                         if (pieceColor == WHITE) k = -k;
  1740.                         if (k == PAWN) {
  1741.                             k = boardPtr[square - j];
  1742.                             if (pieceColor == WHITE) k = -k;
  1743.                             if (k == PAWN) ++r;
  1744.                         }
  1745.                     }
  1746.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1747.                         j += 10;
  1748.                         if (pieceColor == WHITE) j -= 20;
  1749.                         k = boardPtr[j];
  1750.                         if (k == OBNDS) break;
  1751.                         if (pieceColor == WHITE) k = -k;
  1752.                         if (k == PAWN) r -= 4;
  1753.                     }
  1754.                     if ((c == 3) || (c == 4)) {    /* Give negative weight for backward center pawns. */
  1755.                         for (j = square - 1;;) {
  1756.                             j += 10;
  1757.                             if (pieceColor == WHITE) j -= 20;
  1758.                             k = boardPtr[j];
  1759.                             if (k == OBNDS) break;
  1760.                             if (pieceColor == WHITE) k = -k;
  1761.                             if (k == PAWN) {
  1762.                                 r -= 2;
  1763.                                 break;
  1764.                             }
  1765.                         }
  1766.                         for (j = square + 1;;) {
  1767.                             j += 10;
  1768.                             if (pieceColor == WHITE) j -= 20;
  1769.                             k = boardPtr[j];
  1770.                             if (k == OBNDS) break;
  1771.                             if (pieceColor == WHITE) k = -k;
  1772.                             if (k == PAWN) {
  1773.                                 r -= 2;
  1774.                                 break;
  1775.                             }
  1776.                         }
  1777.                     }
  1778.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1779.                         j += 10;
  1780.                         if (pieceColor == WHITE) j -= 20;
  1781.                         k = boardPtr[j];
  1782.                         if (k == OBNDS) break;
  1783.                         if (pieceColor == WHITE) k = -k;
  1784.                         if (k == PAWN) r -= 4;
  1785.                     }
  1786.  
  1787.                     if ((*game)->doc.gameIndex == 2)
  1788.                         if (square == 54)
  1789.                             if ((boardPtr[63] == WP) || (boardPtr[65] == WP))
  1790.                                 r = -200;
  1791.                                     /* Special-case out center-counter. */
  1792.  
  1793.                     if ((*game)->doc.gameIndex == 4)
  1794.                         if (square == 45)
  1795.                             if (boardPtr[45] == BP)
  1796.                                 if (boardPtr[54] == BP)
  1797.                                     if (boardPtr[63] == WP)
  1798.                                         if (boardPtr[64] == WP)
  1799.                                             r += (0x00010000 >> 3);
  1800.                                                 /* Give weight to declining queen's gambit. */
  1801.  
  1802.                     if ((*game)->doc.gameIndex == 4)
  1803.                         if (square == 53)
  1804.                             if (boardPtr[53] == BP)
  1805.                                 if (boardPtr[43] == BN)
  1806.                                     if (boardPtr[65] == WP)
  1807.                                         if (boardPtr[76] == WN)
  1808.                                             r += (0x00010000 >> 3);
  1809.                                                 /* Play a better e4,c5,Nf3. */
  1810.  
  1811.                     if ((*game)->doc.gameIndex == 5)
  1812.                         if (square == 54)
  1813.                             if (boardPtr[54] == WP)
  1814.                                 if (boardPtr[53] == BP)
  1815.                                     if (boardPtr[63] == WP)
  1816.                                         if (boardPtr[46] == BN)
  1817.                                             r += (0x00010000 >> 2);
  1818.                                                 /* Play a better d4,Nf6,c4,c5. */
  1819.  
  1820.                     if ((*game)->doc.gameIndex == 6)
  1821.                         if (square == 64)
  1822.                             if (boardPtr[64] == BP)
  1823.                                 if (boardPtr[43] == BN)
  1824.                                     if (boardPtr[65] == WP)
  1825.                                         if (boardPtr[76] == WN)
  1826.                                             r += (0x00010000 >> 2);
  1827.                                                 /* Play a better e4,c5,Nf3,Nc6,d4. */
  1828.  
  1829.                     if (color != pieceColor) r = -r;
  1830.                     posVal += r;
  1831.  
  1832.                 }
  1833.  
  1834.                 if (piece == KNIGHT) {        /* Give weight to centralized knights. */
  1835.                     if ((!r) || (r == 7)) {
  1836.                         r = -4;
  1837.                         if ((*game)->doc.gameIndex < 3) {
  1838.                             r = 200;
  1839.                             /* Special-case out knights early, 'cause it's really gross. */
  1840.                         }
  1841.                     }
  1842.                     else {
  1843.                         if (r == 4) r = 5;
  1844.                         if (r > 3) r = 7 - r;
  1845.                         if ((c < 2) || (c > 5))  r /= 2;
  1846.                         if ((c < 1) || (c > 6))  r /= 2;
  1847.                     }
  1848.                     if (color != pieceColor) r = -r;
  1849.                     posVal += r;
  1850.                 }
  1851.  
  1852.                 if (piece == BISHOP) {
  1853.                     j = r;
  1854.                     if (r > 5) r = 7 - r;
  1855.                         if (!r) r = -2;        /* Get those bishops developed. */
  1856.                     if ((c < 1) || (c > 6))  r /= 4;
  1857.                     if (j > 1) {        /* Give weight to knights before bishops past 2nd rank. */
  1858.                         c = (c < 4) ? 2 : 7;
  1859.                         k = boardPtr[90 + c - 70 * pieceColor];
  1860.                         if (pieceColor == WHITE) k = -k;
  1861.                         if (k == KNIGHT) r = -1;
  1862.                     }
  1863.                     if (color != pieceColor) r = -r;
  1864.                     posVal += r;
  1865.                 }
  1866.  
  1867.                 if (piece == ROOK) {    /* Give weight to rooks on open files. */
  1868.                     if (material > 25) {
  1869.                         delta = (color == BLACK) ? 10 : -10;
  1870.                         if ((c) && (c < 7)) {
  1871.                             for (j = square + delta; !boardPtr[j]; j += delta, ++r) {};
  1872.                             if (color != pieceColor) r = -r;
  1873.                             posVal += r;
  1874.                         }
  1875.                     }
  1876.                 }
  1877.  
  1878.                 if (piece == QUEEN) {
  1879.                     if (color == pieceColor) {
  1880.                         if (movedPiece == QUEEN) {
  1881.                             if (material > 50) {
  1882.                                 r = -2;
  1883.                                 if (color != pieceColor) r = -r;
  1884.                                 posVal += r;
  1885.                             }        /* Keep the queen from moving too much in the beginning. */
  1886.                         }
  1887.                     }
  1888.                 }
  1889.                 if (piece == KING) {    /* Give weight to no king moves other than castling. */
  1890.                     j = 0;
  1891.                     if ((*game)->doc.king[color].kingMoves < 2) {
  1892.                         j = -10;
  1893.                         if (!r) {
  1894.                             if (c == 6) j = 6;
  1895.                             if (c == 2) j = 4;
  1896.                         }
  1897.                     }
  1898.                     if (color != pieceColor) j = -j;
  1899.                     posVal += j;
  1900.                 }
  1901.  
  1902.                 if (color == pieceColor) {
  1903.  
  1904.                     for (dirNum = 0; (dir = direction[piece][dirNum]) != 0; ++dirNum) {
  1905.                         for (s = square, dist = 1; dist <= distance[piece]; ++dist) {
  1906.                             if (piece == PAWN) {
  1907.                                 if (dirNum > 1) break;
  1908.                                 dir = direction[BISHOP][dirNum];
  1909.                                 if (color == BLACK) dir = -dir;
  1910.                             }
  1911.                             s += dir;
  1912.                             if ((take = (*game)->doc.theBoard[s]) == EMPTY) continue;
  1913.                             if (take == OBNDS) break;
  1914.                             xcolor = BLACK;
  1915.                             if (take < 0) {
  1916.                                 xcolor = WHITE;
  1917.                                 take = -take;
  1918.                             }
  1919.                             if (xcolor == color) break;
  1920.                             switch (j = take) {
  1921.                                 case KNIGHT:
  1922.                                     j = 3;
  1923.                                     break;
  1924.                                 case ROOK:
  1925.                                     j = 5;
  1926.                                     break;
  1927.                                 case QUEEN:
  1928.                                     j = 9;
  1929.                                     break;
  1930.                                 case KING:
  1931.                                     j = material;
  1932.                                     if (j > 39) j = 60 - j;
  1933.                                     j /= 2;
  1934.                                     break;
  1935.                             }
  1936.                             switch (k = piece) {
  1937.                                 case KNIGHT:
  1938.                                     k = 3;
  1939.                                     break;
  1940.                                 case ROOK:
  1941.                                     k = 5;
  1942.                                     break;
  1943.                                 case QUEEN:
  1944.                                     k = 9;
  1945.                                     break;
  1946.                                 case KING:
  1947.                                     k = material;
  1948.                                     if (k > 39) k = 60 - k;
  1949.                                     k /= 2;
  1950.                                     break;
  1951.                             }
  1952.                             if (j > k) posVal += (j - k) * 5;
  1953.                             else if (!SquareAttacked(game, s, color)) posVal += (j + 4);
  1954.                             break;
  1955.                         }
  1956.                     }        /* The above code calculates the aggression factor of the board.
  1957.                             ** If the attacking piece is smaller than the piece attacked, then
  1958.                             ** we add the delta value as a plus.  This factor finds forks,
  1959.                             ** chases kings, queens, etc. */
  1960.  
  1961.                             /* The below code estimates the exchange value of takes.  This
  1962.                             ** is only an estimate, as it is not an actual move analysis
  1963.                             ** of all the possible exchanges.  We are only trying to get
  1964.                             ** an estimate of the board position in this function. */
  1965.  
  1966.                     takerLoc = SquareAttacked(game, square, color);
  1967.                     if (takerLoc) {
  1968.                         saveBoard = false;
  1969.                         xnum = val = 0;
  1970.                         for (xcolor = color ^ 1; takerLoc; xcolor ^= 1) {
  1971.                             take = boardPtr[square];
  1972.                             if (take < 0) take = -take;
  1973.                             if (take == KING)   take = 512;
  1974.                             if (take == QUEEN)  take = 9;
  1975.                             if (take == ROOK)   take = 5;
  1976.                             if (take == KNIGHT) take = 3;
  1977.                             retakerLoc = SquareAttacked(game, square, xcolor);
  1978.                             if (xcolor == color) val += take;
  1979.                             else                 val -= take;
  1980.                             xcng[xnum++] = val;
  1981.                             if (retakerLoc) {
  1982.                                 if (!saveBoard) {
  1983.                                     saveBoard = true;
  1984.                                     for (j = START_IBNDS; j < END_IBNDS; ++j)
  1985.                                         keepBoard[j] = boardPtr[j];
  1986.                                 }
  1987.                                 boardPtr[square] = boardPtr[takerLoc];
  1988.                                 boardPtr[takerLoc] = 0;
  1989.                             }
  1990.                             takerLoc = retakerLoc;
  1991.                         }
  1992.                         if (saveBoard)
  1993.                             for (j = START_IBNDS; j < END_IBNDS; ++j)
  1994.                                 boardPtr[j] = keepBoard[j];
  1995.                         xcng[xnum] = xcng[xnum - 1];
  1996.                         xcolor = 1;        /* Check on opponent first. */
  1997.                         for (loop = true; loop; xcolor ^= 1) {
  1998.                             val  = xcng[xcolor];
  1999.                             k    = xnum;
  2000.                             loop = false;
  2001.                             for (j = xcolor + 2; j <= k; j += 2) {
  2002.                                 if (xcolor) {
  2003.                                     if (val > xcng[j]) {
  2004.                                         val = xcng[j];
  2005.                                         xnum = j - 1;
  2006.                                         loop = true;
  2007.                                     }
  2008.                                 }
  2009.                                 else {
  2010.                                     if (val < xcng[j]) {
  2011.                                         val = xcng[j];
  2012.                                         xnum = j - 1;
  2013.                                         loop = true;
  2014.                                     }
  2015.                                 }
  2016.                             }
  2017.                         }
  2018.                         if (xcngVal > val) xcngVal = val;
  2019.                     }
  2020.                 }
  2021.             }
  2022.         }
  2023.     }
  2024.  
  2025.     val = GameStatus(game);
  2026.     j = (100 - (*game)->doc.numLegalMoves) / 3;
  2027.     if (material > 60) j = 0;
  2028.  
  2029.     xcolor = color ^ 1;
  2030.     for (numChecks = 0;; ++numChecks) {
  2031.         if (!(SquareAttacked(game, (*game)->doc.king[xcolor].kingLoc, xcolor))) break;
  2032.         if ((*game)->doc.gameIndex < 2) break;
  2033.         UnmakeMove(game);
  2034.         UnmakeMove(game);
  2035.     }
  2036.     if (numChecks > 3) {
  2037.         j = 0;
  2038.     }
  2039.     for (; numChecks; --numChecks) {
  2040.         MakeMove(game, 1, 0, 0);
  2041.         MakeMove(game, 1, 0, 0);
  2042.     }
  2043.  
  2044.     posVal += j;
  2045.  
  2046.     if ((val >= kStalemate) && (val <= kDrawByRep)) matBalance = xcngVal = posVal = 0;
  2047.         /* Drawing may be our best move, so don't just eliminate this move. */
  2048.  
  2049.     if (gPosReps) matBalance = xcngVal = posVal = 0;
  2050.         /* Drawing may be our best move, so don't just eliminate this move. */
  2051.  
  2052.     UnmakeMove(game);
  2053.     GenerateLegalMoves(game);
  2054.  
  2055.     value = (((long)(matBalance + xcngVal)) << 16) + (((long)posVal) << 3);
  2056.     if (material > 29) value += (Random() & 0x7F);
  2057.  
  2058.     return(value);        
  2059. }
  2060.  
  2061.  
  2062.  
  2063. /*****************************************************************************/
  2064.  
  2065.  
  2066.  
  2067. #pragma segment Chess
  2068. void    SlideThePiece(FileRecHndl game, short fromSq, short toSq)
  2069. {
  2070.     short    fromRow, fromCol, toRow, toCol;
  2071.     Point    fromLoc, toLoc;
  2072.     Rect    fromRect;
  2073.  
  2074.     fromRow = (fromSq - START_IBNDS) / 10;
  2075.     fromCol = fromSq - START_IBNDS - 10 * fromRow;
  2076.     if ((*game)->doc.invertBoard) {
  2077.         fromRow = 7 - fromRow;
  2078.         fromCol = 7 - fromCol;
  2079.     }
  2080.     fromRect.top    = 1 + fromRow * kBoardSqSize;
  2081.     fromRect.left   = 1 + fromCol * kBoardSqSize;
  2082.     fromRect.bottom = fromRect.top  + 32;
  2083.     fromRect.right  = fromRect.left + 32;
  2084.  
  2085.     toRow = (toSq - START_IBNDS) / 10;
  2086.     toCol = toSq - START_IBNDS - 10 * toRow;
  2087.     if ((*game)->doc.invertBoard) {
  2088.         toRow = 7 - toRow;
  2089.         toCol = 7 - toCol;
  2090.     }
  2091.  
  2092.     fromLoc.v = fromRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  2093.     fromLoc.h = fromCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  2094.     toLoc.v   = toRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  2095.     toLoc.h   = toCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  2096.  
  2097.     MoveThePiece(game, fromSq, fromRect, fromLoc, &toLoc);
  2098. }
  2099.  
  2100.  
  2101.  
  2102. /*****************************************************************************/
  2103.  
  2104.  
  2105.  
  2106. #pragma segment Chess
  2107. void    CalcPositionValues(FileRecHndl game)
  2108. {
  2109.     short    *boardPtr, square, piece;
  2110.     long    val;
  2111.  
  2112.     boardPtr = &(*game)->doc.theBoard[0];
  2113.     gTreeValue = gWhiteTotal = gBlackTotal = gNumPieces = 0;
  2114.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  2115.         piece = boardPtr[square];
  2116.         if (piece) {
  2117.             if (piece != OBNDS) {
  2118.                 val = gTreePieceValues[piece + KING];
  2119.                 gTreeValue += val;
  2120.                 if (piece < 0) piece = -piece;
  2121.                 if (piece != KING) {
  2122.                     gPieceLoc = square;
  2123.                     ++gNumPieces;
  2124.                     if (val < 0) gWhiteTotal -= val;
  2125.                     else         gBlackTotal += val;
  2126.                 }
  2127.             }
  2128.         }
  2129.     }
  2130. }
  2131.  
  2132.  
  2133.  
  2134.